//
//  EMNetClient.m
//  EasyMoney
//
//  Created by zh on 2019/12/14.
//  Copyright © 2019 zh. All rights reserved.
//

#import "EMNetClient.h"
#import "EMRefreshTokenRequestModel.h"
#import "EMRefreshTokenResponseModel.h"
#import "EMNetConfig.h"

static const NSInteger UnknowErrorCode = -90000;
static const NSInteger SuccessCode = 0;//成功
static const NSInteger accessTokenInvalid = 5001;
static const NSInteger refreshTokenInvalid = 5002;
static const NSInteger accessTokenMissing = 1011;
static const NSInteger accessTokenUnused = 1012;
//static const NSInteger FailureCode = 1;//失败
//static const NSInteger ErrorCode = 1;//错误

NSString * const RRZErrorDomain = @"com.kmad.crms.error.domain";
static NSString * const JSONResponseSerializerWithDataKey = @"JSONResponseSerializerWithDataKey";

@interface EMJSONResponseSerializer : AFJSONResponseSerializer

@end

@implementation EMJSONResponseSerializer

- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError * _Nullable __autoreleasing *)error
{
    id resp = [super responseObjectForResponse:response data:data error:error];
    if (*error != nil) {
        NSMutableDictionary *userInfo = [(*error).userInfo mutableCopy];
        if (data != nil) {
            userInfo[JSONResponseSerializerWithDataKey] = data;
        }
        
        NSError *newError = [NSError errorWithDomain:(*error).domain code:(*error).code userInfo:userInfo];
        (*error) = newError;
    }
    return resp;
}

@end

@interface EMNetClient ()

@end

@implementation EMNetClient

#pragma mark- lazy load
- (AFHTTPSessionManager *)netManager
{
    if (!_netManager) {
        NSURL* baseUrl = [EMNetConfig sharedConfig].baseURL;
        AFHTTPSessionManager* sessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseUrl];
        
        AFJSONRequestSerializer *jsonRequest = [AFJSONRequestSerializer serializer];
        sessionManager.requestSerializer = jsonRequest;
        [sessionManager.requestSerializer setTimeoutInterval:[self timeout]];
        
        EMJSONResponseSerializer* jsonResponse = [EMJSONResponseSerializer serializer];
        jsonResponse.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html", @"multipart/form-data",nil];
        sessionManager.responseSerializer = jsonResponse;

#ifdef DEBUG
          AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
          securityPolicy.allowInvalidCertificates = YES;//是否信任非法证书(自建证书)
          securityPolicy.validatesDomainName = NO;//是否验证域名有效性
          sessionManager.securityPolicy = securityPolicy;
#endif

        _netManager = sessionManager;
    }
    return _netManager;
}

#pragma mark- init
+ (instancetype)sharedClient {
    static EMNetClient *_sharedClient = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedClient = [[EMNetClient alloc] init];
    });
    return _sharedClient;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.timeout = 10;
    }
    return self;
}

#pragma mark- public
- (void)requestWithMethodKey:(NSString*)methodKey isPost:(BOOL)isPost param:(id)param success:(void(^)(id responseObject, NSString* message))success failed:(void(^)(NSInteger errorCode, NSString* errorMessage, id errorData))failed {
    [self _requestWithMethodKey:methodKey isPost:isPost param:param success:success failed:^(NSInteger errorCode, NSString *errorMessage, id errorData) {
        if (errorCode == accessTokenInvalid || errorCode == accessTokenMissing || errorCode == accessTokenUnused) {//accessToken过期
            //token失效
            EMRefreshTokenRequestModel *req = [[EMRefreshTokenRequestModel alloc] init];
            req.refreshToken = [[EMNetConfig sharedConfig] getRefreshToken];
            [self _requestWithMethodKey:req.requestMethodKey isPost:req.isPostForRequest param:[req params] success:^(id responseObject, NSString *message) {
                if ([responseObject isKindOfClass:[NSDictionary class]]) {
                    EMRefreshTokenResponseModel *respObj = [EMRefreshTokenResponseModel yy_modelWithJSON:responseObject];
                    if (respObj) {
                        //更新token并保存
                        [[EMNetConfig sharedConfig] setToken:SAFE_STRING(respObj.accessToken)];
                        [[EMNetConfig sharedConfig] setRefreshToken:SAFE_STRING(respObj.refreshToken)];
                        [self _requestWithMethodKey:methodKey isPost:isPost param:param success:success failed:failed];
                    } else {
                        if (failed) {
                            failed(errorCode,errorMessage,errorData);
                        }
                    }
                } else {
                    if (failed) {
                        failed(errorCode,errorMessage,errorData);
                    }
                }
            } failed:^(NSInteger errorCode, NSString *errorMessage, id errorData) {
                if (errorCode == refreshTokenInvalid) {
                    [[NSNotificationCenter defaultCenter] postNotificationName:NetClientRefreshTokenInvalid object:nil];
                }
            }];
        } else if (errorCode == refreshTokenInvalid) {
            [[NSNotificationCenter defaultCenter] postNotificationName:NetClientRefreshTokenInvalid object:nil];
        } else {
            if (failed) {
                failed(errorCode,errorMessage,errorData);
            }
        }
    }];
}

- (void)_requestWithMethodKey:(NSString*)methodKey isPost:(BOOL)isPost param:(id)param success:(void(^)(id responseObject, NSString* message))success failed:(void(^)(NSInteger errorCode, NSString* errorMessage, id errorData))failed {
    void(^unexpectErrorFailed)(NSURLSessionDataTask *task,NSError *error) = ^(NSURLSessionDataTask *task,NSError *error) {
        //原因不明或者系统性错误，统一报错文案。
        if (failed) {
            NSString* errorMessage = NSLocalizedString(@"网络错误", nil);
            NSInteger errorCode = error.code;
            id errorData = error;
            if (error.userInfo[JSONResponseSerializerWithDataKey]) {
                NSData *data = error.userInfo[JSONResponseSerializerWithDataKey];
                NSDictionary * serializedData = nil;
                if (data != nil) {
                    serializedData = [NSJSONSerialization JSONObjectWithData: data options:kNilOptions error:nil];
                    if (serializedData) {
                        NSMutableDictionary* mutDit = [[NSMutableDictionary alloc]initWithDictionary:serializedData];
                        errorMessage = [mutDit objectForKey:@"errorMessage"];
                        if ([mutDit objectForKey:@"errorCode"]) {
                            errorCode = [[[mutDit objectForKey:@"errorCode"] description] integerValue];
                        }
                        errorData = mutDit;
                    }
                }
            }
        NSLog(@"\nResp Error:\nUrl:%@\nerror:%@\n",task.response.URL.absoluteString,error);
            failed(errorCode,errorMessage,errorData);
        }
    };

    void(^sessionSuccessBlock)(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) = ^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (responseObject != nil) {
#ifdef DEBUG
            NSData *tempDisplay = [NSJSONSerialization dataWithJSONObject:responseObject options:NSJSONWritingPrettyPrinted error:nil];
            NSString *tempString =[[NSString alloc] initWithData:tempDisplay encoding:NSUTF8StringEncoding];
            NSLog(@"\n============ Response: ===============\nUrl:%@\nAPI方法:%@\n%@\n\n",task.response.URL.absoluteString,methodKey,tempString);
#endif
            NSString* errorMsg = [responseObject objectForKey:@"errorMessage"];
            NSInteger code = [[[responseObject objectForKey:@"errorCode"] description] integerValue];
            BOOL isSuccess = (code == SuccessCode);//code==0成功
            if (!isSuccess) {
                //请求成功，但数据为业务上定义的失败
                if (failed) {
                    failed(code,errorMsg,responseObject);
                }
            } else {
                //请求成功，且有结果
                if (success) {
                    success(responseObject,errorMsg);
                }
            }
        } else {
            //网络请求成功，无返回对应的数据结构,按照错误处理
            unexpectErrorFailed(task,nil);
        }
    };
    void(^sessionFailedBlock)(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) = ^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        //系统网络请求错误
        unexpectErrorFailed(task,error);
    };

    NSLog(@"\n==========Send Request:==============\nserver:%@\nisPost:%d\n发送信息:\nAPI方法:%@\n%@\n\n",[EMNetConfig sharedConfig].baseURL.absoluteString,isPost,methodKey,param);
    [self updateHeaderInfo];
    if (YES == isPost) {
        [self.netManager POST:methodKey parameters:param progress:nil success:sessionSuccessBlock failure:sessionFailedBlock];
    } else {
        [self.netManager GET:methodKey parameters:param progress:nil success:sessionSuccessBlock failure:sessionFailedBlock];
    }
}

- (void)uploadWithMethodKey:(NSString*)methodKey constructBlock:(void (^)(id <AFMultipartFormData> formData))constructBlock param:(id)param progress:(void(^)(NSProgress *))progress success:(void(^)(id responseObject, NSString* message))success failed:(void(^)(NSInteger errorCode, NSString* errorMessage, id errorData))failed
{
    void(^unexpectErrorFailed)(NSURLSessionDataTask *task,NSError *error) = ^(NSURLSessionDataTask *task,NSError *error) {
        //原因不明或者系统性错误，统一报错文案。
        if (failed) {
            NSLog(@"\nResp Error:\nUrl:%@\nerror:%@\n",task.response.URL.absoluteString,error);
            failed(error?error.code:UnknowErrorCode,@"网络错误",error);
        }
    };

    void(^sessionSuccessBlock)(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) = ^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (responseObject != nil) {
#ifdef DEBUG
            NSData *tempDisplay = [NSJSONSerialization dataWithJSONObject:responseObject options:NSJSONWritingPrettyPrinted error:nil];
            NSString *tempString =[[NSString alloc] initWithData:tempDisplay encoding:NSUTF8StringEncoding];
            NSLog(@"\nResponse:\nUrl:%@\nAPI方法:%@\n%@\n\n",task.response.URL.absoluteString,methodKey,tempString);
#endif
            NSString* errorMsg = [responseObject objectForKey:@"errorMessage"];
            NSInteger code = [[[responseObject objectForKey:@"errorCode"] description] integerValue];
            BOOL isSuccess = (code == SuccessCode);//code==0成功
            if (!isSuccess) {
                //请求成功，但数据为业务上定义的失败
                if (failed) {
                    failed(code,errorMsg,responseObject);
                }
            } else {
                //请求成功，且有结果
                if (success) {
                    success(responseObject,errorMsg);
                }
            }
        } else {
            //网络请求成功，无返回对应的数据结构,按照错误处理
            unexpectErrorFailed(task,nil);
        }
    };
    void(^sessionFailedBlock)(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) = ^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        //系统网络请求错误
        unexpectErrorFailed(task,nil);
    };

    NSLog(@"\n====== Send Request Start: =========\nserver:%@\nisPost:%d\n发送信息:\nAPI方法:%@\n%@\n====== Send Request End =========",[EMNetConfig sharedConfig].baseURL.absoluteString,1,methodKey,param);
    [self updateHeaderInfo];
    [self.netManager POST:methodKey parameters:param constructingBodyWithBlock:constructBlock progress:progress success:sessionSuccessBlock failure:sessionFailedBlock];
}

#pragma mark- private

- (void)updateHeaderInfo
{
    NSString *deviceId = [[EMNetConfig sharedConfig] getOpenUUID];
    [self.netManager.requestSerializer setValue:SAFE_STRING([[EMNetConfig sharedConfig] getToken]) forHTTPHeaderField:@"Authorization"];
    [self.netManager.requestSerializer setValue:SAFE_STRING(deviceId) forHTTPHeaderField:@"deviceId"];
    [self.netManager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [self.netManager.requestSerializer setValue:[[EMNetConfig sharedConfig] agentString] forHTTPHeaderField:@"User-Agent"];
}


@end
